#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File  : Login.py
# @Author: shadow
# @Date  : 2019/12/3
# @Desc  : Login page

from tkinter import *
import tkinter.messagebox as ma
from collections import namedtuple
from menu import MenuPage
import userdata as userdata
import re
import hashlib

NAME = 1  # 用户名
PWD = 2   # 密码
PWDF = 3  # 确认密码
TEL = 4   # 电话号码
Input = namedtuple("input", "name, password, pwdconfirm, telephone")
input_prompt = ["输入用户名\n按回车确认", "请输入密码\n按回车确认", "输确认密码\n按回车确认", "请输入电话\n按回车确认",
                "信息已确认\n按注册按钮", "请输入密码\n按回车确认", "信息已确认\n按登陆按钮"]


# 电话号码检查，11位以内，数字
def telethone_check(content: str):
    if content.isdigit() and len(content) < 12 or content == "":
        return True
    else:
        return False


# 登陆/注册 界面类
class LoginPage(object):
    def __init__(self, window=None, title="ATM-Login"):
        self.window = window
        self.usersInfo = userdata.get_users()    # 获取用户
        self.window.geometry("320x200")
        self.window.title(title)
        self.input = Input(StringVar(), StringVar(), StringVar(), StringVar())
        self.showStr = "*"
        self.state = [NORMAL, DISABLED, DISABLED, DISABLED, DISABLED]  # 注册时要按顺序输入
        self.sign_times = 0     # 登陆错误次数
        self.prompt = StringVar()   # 登陆提示信息
        self.page_init()
        self.select_page()

    def page_init(self):
        for i in range(4):
            self.input[i].set("")
        self.state = [NORMAL, DISABLED, DISABLED, DISABLED, DISABLED]  # 初始化输入状态
        self.prompt.set(input_prompt[0])

    # 初始选择界面
    def select_page(self):
        self.page = Frame(self.window)
        self.page.pack()
        Label(self.page, text="欢迎使用ATM:").grid(row=0, stick=W)
        Button(self.page, text="注册", height=2, bg='green', width=10, command=self.signup_page)\
            .grid(row=1, column=1, pady=5)
        Button(self.page, text="登陆", height=2, bg='blue', width=10, command=self.signin_page)\
            .grid(row=2, column=1, pady=5)
        Button(self.page, text="退出", width=7, bg='red', command=self.page.quit).grid(row=3, column=2, padx=5)

    # 登陆界面布局
    def signin_page(self, ent=0):
        self.page.destroy()  # 销毁之前的布局
        # self.input.name.set("shadow")
        # self.input.name.set("admin")
        # self.input.password.set("123456")

        self.page = Frame(self.window)  # 重新生成一个布局
        self.page.pack()
        entry_list = []

        Checkbutton(self.page, bd=4, text="显示密码", activebackground="gray", command=self.show_pwd).grid(row=1)
        Label(self.page, textvariable=self.prompt, bg="yellow").grid(row=1, column=1)
        Label(self.page, height=2, text="用户名:").grid(row=2)
        Label(self.page, height=2, text="密  码:").grid(row=3)
        entry_list.append(Entry(self.page, width=12, textvariable=self.input.name))
        entry_list.append(Entry(self.page, width=12, show=self.showStr, textvariable=self.input.password))
        entry_list[0].grid(row=2, column=1, pady=10, stick=W)
        entry_list[1].grid(row=3, column=1)
        Button(self.page, text="登陆", height=2, bg="lime", width=8, command=self.sign_in).grid(row=2, column=2, rowspan=2)
        Button(self.page, text="返回", bg="blue", width=8, command=self.refresh).grid(row=4, pady=5)
        Button(self.page, text="退出", bg="red", width=8, command=self.page.quit).grid(row=4, column=2, padx=10)

        if ent < 2:
            entry_list[ent].bind("<Return>", lambda x: self.refresh(ent+5))
            entry_list[ent].focus_set()

    # 注册界面布局
    def signup_page(self, ent=0):
        self.page.destroy()
        self.page = Frame(self.window)
        self.page.pack()

        input_tel = self.window.register(telethone_check)  # 需要将函数包装一下，必要的
        entry_list = []
        # Label(self.page, text="带*项必填").grid()
        Label(self.page, text="*用户名:").grid(row=1, stick=W, pady=5)
        Label(self.page, text="*密  码:").grid(row=2, stick=W, pady=5)
        Label(self.page, text="*确认密码:").grid(row=3, stick=W, pady=5)
        Label(self.page, text="手机号:").grid(row=4, stick=W, pady=5)
        Label(self.page, textvariable=self.prompt, bg="yellow").grid(row=1, column=2)

        for i in range(3):
            entry_list.append(Entry(self.page, width=12, textvariable=self.input[i], state=self.state[i]))
            entry_list[i].grid(row=i+1, column=1, stick=W)
        entry_list.append(Entry(self.page, width=12, state=self.state[3], textvariable=self.input.telephone,
              validate='key', validatecommand=(input_tel, '%P'),
              invalidcommand=lambda: self.refresh(7)))  # 发生变动即检查，必须是数字
        entry_list[3].grid(row=4, column=1, stick=W)

        Button(self.page, text="注册", bg="lime", fg="red", width=8, height=2, state=self.state[4],
               command=self.sign_up).grid(row=2, rowspan=3, column=2, padx=10)
        Button(self.page, text="返回", bg="blue", width=8, command=self.refresh).grid(row=5, padx=10, pady=8, stick=E)
        Button(self.page, text="退出", bg="red", width=8, command=self.page.quit).grid(row=5, column=2, stick=W)

        if ent < 4:
            entry_list[ent].bind("<Return>", lambda x: self.refresh(ent+1))
            entry_list[ent].focus_set()

    # 密码的显示与不现实的切换
    def show_pwd(self):
        if self.showStr:
            self.showStr = ""
        else:
            self.showStr = "*"
        self.page.destroy()
        self.signin_page()

    # 刷新页面, 默认返回到主页面
    def refresh(self, ent=0):
        if ent:   # 1-4 注册输入 ， 5-6 登陆输入, 7 电话号码提示
            if ent > 6:
                self.prompt.set("电话号码\n必须是数字")
                self.page.destroy()
                self.signup_page(ent - 4)
            elif ent > 4:
                    self.prompt.set(input_prompt[ent])
                    self.page.destroy()
                    self.signin_page(ent-4)
            else:
                if self.input_check(ent):   # 输入信息合理
                    self.state[ent-1], self.state[ent] = "readonly", NORMAL
                    self.prompt.set(input_prompt[ent])
                    self.page.destroy()
                    self.signup_page(ent)
                else:   # 输入信息不合要求
                    self.input[ent-1].set("")  # 清空输入信息
        else:   # 返回主界面，清空所有输入信息
            self.page.destroy()
            self.page_init()
            self.select_page()

    # 检查输入信息是否合理
    def input_check(self, ent):
        content = self.input[ent-1].get()
        # print(content, ent)
        if ent == NAME:
            if 6 > len(content) or len(content) > 20:
                # ma.showwarning("Warning", f"Lenght of username needs to be between 6 to 20 !")
                self.prompt.set("用户名长度\n不符合要求")
                return False
            elif userdata.find_user(content):
                # ma.showwarning("Warning", f"Username [{content}] already exists, try other please !")
                self.prompt.set("用户已存在\n请试试其它")
                return False
            else:
                return True
        elif ent == PWD:  # 使用正则检查密码, 可设置密码难度
            pwd_rule = re.compile(r'^\w{8,}$')  # 密码要求：8位以上，数字，字母，下划线，中文
            # pwd_rule = re.compile(r"^(?!\d.*)(?!.*\W)(?![A-Za-z_]+$)(?![a-z0-9_]+$)(?![A-Z0-9_]+$)^.{8,}$", src)
            if pwd_rule.match(content):
                return True
            else:
                self.prompt.set("密码不合理\n请重新输入")
                return False
        elif ent == PWDF:  # 确认密码，需要和上面的相同
            if self.input.password.get() == content:
                return True
            else:
                # ma.showwarning("Warning", f"Two password is not same!")
                self.prompt.set("密码不相同\n请确认输入")
                return False
        elif ent == TEL:
            if len(content) > 6:
                return True
            else:
                self.prompt.set("电话长度\n不合理")
                return False
        else:
            return False

    # 注册用户处理
    def sign_up(self):
        name = self.input.name.get()
        pwd = self.input.password.get()
        tel = self.input.telephone.get()

        # 符合要求，写入csv数据表
        num = len(self.usersInfo) + 1
        md5 = hashlib.md5()
        md5.update(pwd.encode("utf8"))
        pwd_md5 = md5.hexdigest()
        # print(md5.hexdigest())
        if userdata.add_user([num, name, pwd_md5, 5000, tel, 1]):  # 注册就送5000余额
            msg = f"SUCCESS\n  姓名: {name}\n  账号: {num}\n  密码: {pwd}\n  余额: 5000\n  电话: {tel}"
            if ma.showinfo("SING_UP", msg):  # 点击确定，返回到选择界面
                self.refresh()
        for i in range(4):
            self.input[i].set("")

    # 登陆验证
    def sign_in(self):
        name = self.input.name.get()
        passwd = self.input.password.get()
        if not name:
            ma.showwarning("Warning", "Please input username !")
        elif not passwd:
            ma.showwarning("Warning", "Please input password!")
        else:
            self.usersInfo = userdata.get_users()    # 再次获取，预防中间有注册用户
            # print(self.usersInfo)
            for n in self.usersInfo:
                md5 = hashlib.md5()
                md5.update(passwd.encode("utf8"))
                pwd_md5 = md5.hexdigest()
                # print(md5.hexdigest())
                if name == n.name and pwd_md5 == n.passwd:
                    admin = False
                    if name == "admin":  # 管理员用户
                        admin = True
                    self.page.destroy()
                    self.sign_times = 0         # 登陆成功，刷新错误次数
                    MenuPage(n, admin, self.window)    # 将用户信息（命名元组）传给menu
                    break
            else:
                if self.sign_times > 1:  # 尝试第三次错误，直接弹框后退出
                    ma.showerror("Error", "Mistake too many times, please try again later!")
                    self.page.quit()
                else:
                    # ma.showwarning("Warning", "username and password is not match!")
                    self.prompt.set("信息不匹配\n请确认信息")
                    self.sign_times += 1


